#include "wavefront/Model.hpp"

#include <string>
#include <sstream>

#include <boost/lexical_cast.hpp>
#include <boost/regex.hpp>
#include <boost/tokenizer.hpp>
#include <boost/foreach.hpp>

class ibinarystreambuf : public std::streambuf
{
public:
	ibinarystreambuf(unsigned char* data,int size)
	{
		char* p = reinterpret_cast<char*>(data);
		this->setg(p,p,p+size);
	}
};

namespace wavefront{
void Model::clear()
{
	m_vertices.clear();
	m_textureCoords.clear();
	m_normals.clear();
}
void Model::load(unsigned char* data,int size)
{
	typedef boost::char_separator<char> Separator;
	Separator spaceSeparator(" \r","",boost::drop_empty_tokens);
	Separator spaceSlashSeparator("/ \r","",boost::drop_empty_tokens);
	typedef boost::tokenizer<Separator> Tokenizer;

	ibinarystreambuf ibsb(data,size);
	std::istream is(&ibsb);

	struct{
		float operator()(const std::string& str){return boost::lexical_cast<float,std::string>(str);}
	}fcast;

	std::vector<Vertex> v;
	std::vector<TextureCoord> vt;
	std::vector<Normal> vn;

	std::vector<std::string> buf;
	std::string line;
	while(true)
	{
		std::getline(is,line);

		if(is.eof()){break;}

		if(line.length() <= 1)
		{
			continue;
		}

		buf.clear();
		if(line[0] == 'v')
		{
			switch(line[1])
			{
			case ' ':
				{
					Tokenizer tokenizer(line,spaceSeparator);
					BOOST_FOREACH(const std::string& token,tokenizer)
					{
						buf.push_back(token);
					}
					v.push_back(boost::make_tuple(fcast(buf[1]),fcast(buf[2]),fcast(buf[3])));
				}
				break;
			case 't':
				{
					Tokenizer tokenizer(line,spaceSeparator);
					BOOST_FOREACH(const std::string& token,tokenizer)
					{
						buf.push_back(token);
					}
					vt.push_back(boost::make_tuple(fcast(buf[1]),fcast(buf[2])));
				}
				break;
			case 'n':
				{
					Tokenizer tokenizer(line,spaceSeparator);
					BOOST_FOREACH(const std::string& token,tokenizer)
					{
						buf.push_back(token);
					}
					vn.push_back(boost::make_tuple(fcast(buf[1]),fcast(buf[2]),fcast(buf[3])));
				}
				break;
			default:
				break;
			}
		}
		else if(line[0] == 'f')
		{
			struct{
				int operator()(const std::string& str){return boost::lexical_cast<int,std::string>(str);}
			}icast;

			Tokenizer tokenizer(line,spaceSlashSeparator);
			BOOST_FOREACH(const std::string& token,tokenizer)
			{
				buf.push_back(token);
			}

			for(int i = 0 ; i < 3 ; ++i)
			{
				const Vertex& addv = v[ icast(buf[1 + i*3]) - 1 ];
				m_vertices.push_back( addv );
				const TextureCoord& addt = vt[ icast(buf[2 + i*3]) - 1 ];
				m_textureCoords.push_back( addt );
				const Normal& addn = vn[ icast(buf[3 + i*3]) - 1 ];
				m_normals.push_back( addn );
			}
		}
	}
}
}//wavefront
